默认布局:主题暗黑模式事件传递
DarkModeToggle 组件在之前的课程中已经实现过基本功能,现在需要将它集成到 ThemeSettings 的事件体系中。核心问题:ThemeSettings 中的 darkMode 开关如何与 DarkModeToggle 组件同步状态?
两种方案对比
方案一:defineModel 双向绑定
// dark-mode-toggle.vue
const isDark = defineModel<boolean>({ default: false })
typescript
父组件使用 v-model 绑定:
<DarkModeToggle v-model="localSettings.darkMode" />
vue
优点是简洁直观,缺点是 DarkModeToggle 的内部状态被外部完全控制,失去了组件的独立性。
方案二:emit 事件(推荐)
// dark-mode-toggle.vue
const props = defineProps<{ isDark?: boolean }>()
const emit = defineEmits<{ change: [isDark: boolean] }>()
watch(isDarkRef, (val) => {
emit('change', val)
})
typescript
父组件监听 change 事件:
<DarkModeToggle @change="handleDarkChange" />
vue
这种方案保持了 DarkModeToggle 作为独立组件的完整性——它自己管理暗黑模式的切换逻辑,只在状态变化时通知父组件。
事件传递路径
DarkModeToggle (emit: change)
→ Header (转发 change 事件)
→ DefaultLayout (更新 localSettings.darkMode)
→ 通过 provide 传递给所有子组件
text
同时,ThemeSettings 中的暗黑模式开关也需要同步:
ThemeSettings (emit: close, 包含 darkMode 字段)
→ Header (转发 theme-change 事件)
→ DefaultLayout (更新 localSettings)
text
两条路径最终都汇合到 localSettings.darkMode,保证了状态的一致性。
provide/inject 的同步
DefaultLayout 更新 localSettings.darkMode 后,所有通过 inject 获取暗黑状态的子组件都会自动响应:
// default.vue
provide('themeSettings', readonly(localSettings))
// 任意子组件
const settings = inject('themeSettings')
watch(() => settings.darkMode, (val) => {
// 响应暗黑模式变化
})
typescript
本节小结
- 方案选择:推荐使用 emit 事件而非 defineModel,保持 DarkModeToggle 的组件独立性。
- 双向同步:DarkModeToggle 和 ThemeSettings 的暗黑模式开关都通过 DefaultLayout 统一管理。
- provide 响应式:DefaultLayout 使用
provide+readonly传递设置数据,子组件自动响应变化。
↑